`include "defines.v"

module uart_rx #
(
    parameter CLK_FREQ          = 50_000_000,       // 时钟频率50MHz
    parameter BSP               = 9600,             // 波特率9600
    parameter PARITY            = `NONE,            // 奇偶校验位
    parameter DATA_BITS         = 8,                // 数据位bit数
    parameter STOP_BITS         = 1                // 停止位bit数
)
(
    input                       clk_50m,            // 时钟信号
    input                       rst_n,              // 复位信号
    input                       rx,                 // 接收数据信号
    output reg [DATA_BITS-1:0]  q,                  // 输出信号
    output reg                  done,               // 数据接收完成
    output reg                  err,                // 数据错误指示
    output                      busy                // 忙信号，用于对外提示
);

    reg [DATA_BITS-1:0] data = 0;
    reg rx_1, rx_2, rx_3;
    wire en; // 开始接收数据标识 

    reg flag = 0; // 状态保持标识
    reg sampling = 0; // 采样脉冲

    reg [31:0] cnt = 0; // 计数器
    localparam CNT_MAX = CLK_FREQ / BSP; // 传输1bit所需时钟频率

    reg [3:0] cstate = `FSM_IDLE;
    reg [3:0] num = 0;

    reg ifparity = 0;
    reg parity_value = 0;

    // 1、检测开始信号
    always @(posedge clk_50m or negedge rst_n) begin
        if (!rst_n) begin
            rx_1 <= 0;
            rx_2 <= 0;
            rx_3 <= 0;
        end else begin
            rx_1 <= rx;
            rx_2 <= rx_1;
            rx_3 <= rx_2;
        end
    end

    assign en = (rx_2 == 1 && rx_1 == 0) ? 1 : 0; // 得到一个rx从高到低的跳变沿

    // 2、定义一个状态保持寄存器，在接收数据期间保持高电平
    always @(posedge clk_50m or negedge rst_n) begin
        if (!rst_n) flag <= 0;
        else if (en) flag <= 1;
        else if (cstate == `FSM_STOP && sampling == 1) flag <= 0;
        else flag <= flag;
    end

    assign busy = flag;

    // 3、实现计数器
    always @(posedge clk_50m or negedge rst_n) begin
        if (!rst_n) cnt <= 0;
        else if (cnt == CNT_MAX - 1 || flag == 0) cnt <= 0;
        else if (flag) cnt <= cnt + 1;
        else cnt <= 0;
    end

    // 4、脉冲采样
    always @(posedge clk_50m or negedge rst_n) begin
        if (!rst_n) sampling <= 0;
        else if (cnt == CNT_MAX / 2) sampling <= 1;
        else sampling <= 0;
    end

    // 5、状态机
    always @(posedge clk_50m or negedge rst_n) begin
        if (!rst_n) begin
            cstate <= `FSM_IDLE;
            data <= 0;
            num <= 0;
            done <= 0;
            q <= 0;
            err <= 0;
        end else begin
            case (cstate)
                `FSM_IDLE: begin
                    done <= 0;
                    err <= 0;
                    if(flag) cstate <= `FSM_START;
                    else cstate <= `FSM_IDLE;
                end
                `FSM_START: begin
                    if(sampling) cstate <= `FSM_DATA;
                    else cstate <= `FSM_START;
                end
                `FSM_DATA: begin
                    if(sampling && num < DATA_BITS) begin
                        data[num] <= rx_3;
                        num <= num + 1;
                    end
                    else if (num == DATA_BITS) begin
                        cstate <= ifparity ? `FSM_PARITY : `FSM_STOP;
                        num <= 0;
                    end
                    else begin
                        cstate <= `FSM_DATA;
                        num <= num;
                    end
                end
                `FSM_PARITY: begin
                    if(sampling) begin
                        cstate <= `FSM_STOP;
                        if (parity_value == rx_3) err <= 0; // 正常
                        else err <= 1; // 报告错误
                    end
                    else cstate <= `FSM_PARITY;
                end
                `FSM_STOP: begin
                    if(sampling) begin
                        if (rx_3 == 1) begin
                            done <= 1;
                            q <= data;
                            cstate <= `FSM_IDLE;
                        end
                        else err <= 1;
                    end
                    else cstate <= `FSM_STOP;
                end
                default: ;
            endcase
        end
    end

    // 6、设置校验位
    always @(*) begin
        if (!rst_n) begin
            ifparity <= 0;
            parity_value <= 0;
        end
        else begin
            case (PARITY)
                `NONE: begin ifparity <= 0; parity_value <= 0; end
                `ODD: begin ifparity <= 1; parity_value <= ~(^data); end
                `EVEN: begin ifparity <= 1; parity_value <= ^data; end
                `MARK: begin ifparity <= 1; parity_value <= 1; end
                `SPACE: begin ifparity <= 1; parity_value <= 0; end
                default: begin ifparity <= 0; parity_value <= 0; end
            endcase
        end
    end

endmodule  //uart_rx